home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Testing & Debugging / General tools / Audit app & dcmd / Src / Audit.c next >
Encoding:
C/C++ Source or Header  |  1993-09-17  |  32.7 KB  |  1,098 lines  |  [TEXT/KAHL]

  1. /*                                        Audit.c                                    */
  2. #define PARANOIA    0
  3. #include <Packages.h>
  4. /*
  5.  * Audit.c
  6.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  7.  * Programmed by Martin Minow,
  8.  *    Internet:    minow@apple.com
  9.  *    AppleLink:    MINOW
  10.  * Version of January 14, 1993
  11.  *
  12.  * Edit History
  13.  *    93.01.09 MM        First public release
  14.  *    93.01.14 MM        Think and MPW generate different record sizes; a disaster if
  15.  *                    you create an Audit Record under Think and call Audit compiled
  16.  *                    under MPW. Also added a test for record sizes and included
  17.  *                    record size information in the AuditRecord.
  18.  *    93.01.21 MM        Fix bug in procedure name recognizer that failed for certain
  19.  *                    pascal functions.
  20.  *    93.06.10 MM        Cosmetic edits for Think C 6.0. No functional changes. Text
  21.  *                    was reformatted to fit into an "80-column" line.
  22.  *    93.08.14 MM        Added "logicalRAMSize to the AuditRecord. This protects
  23.  *                    Audit (somewhat) against bad data forcing address errors.
  24.  *
  25.  * This library implements a drop-in logging capability for device drivers,
  26.  * callback functions, applications, and all other Macintosh code segments.
  27.  *
  28.  * The overall philosophy is that an application or driver allocates a record in
  29.  * the system heap that contains a small preamble and one or more 48 (decimal)
  30.  * byte blocks of data. The driver logs data to the log area, where each log
  31.  * record contains an identification value and up to four longwords of data.
  32.  *
  33.  * The user would then write a log control application that can turn logging on or
  34.  * off and dump/format the data in the log. The user can also use a MacsBug dcmd
  35.  * to display the current state of the log area.
  36.  *
  37.  * To add logging to a driver, add the following variable to the driver control
  38.  * record:
  39.  *        AuditPtr            auditPtr;
  40.  * and initialize the driver log (in the driver open routine) by calling
  41.  *        devCtlEnt->auditPtr = InitAudit(
  42.  *                        gestaltSelector,
  43.  *                        nEntries,
  44.  *                        initiallyEnabled,
  45.  *                        preserveFirstFlag
  46.  *                    );
  47.  *
  48.  * The intialization routine creates an audit record in the System Heap and
  49.  * defines a gestalt selector with a pointer to the audit area. Once created,
  50.  * the record remains in the heap until the Macintosh is rebooted. The
  51.  * gestaltSelector should be unique to the driver. InitAudit need only be
  52.  * called once by any code segment that can allocate memory (i.e., by an
  53.  * application, INIT, or driver open function). Note that you do not
  54.  * need to check errors: if InitAudit fails to create (or locate) the record,
  55.  * it returns NULL and all other routines, upon receiving a NULL AuditPtr
  56.  * argument, do nothing gracefully.
  57.  *
  58.  * The log record contains a fixed header with the following information:
  59.  *
  60.  *    version.u.low        The earliest version of the AuditRecord that this instance
  61.  *                        of the library understands.
  62.  *    version.u.high        The latest version of the AuditRecord that this instance
  63.  *                        of the library understands.
  64.  *    size.auditRecord    The size of the Audit Record (excluding the dummy first
  65.  *                        entry). This protects against compiler alignment errors
  66.  *                        (which may occur if an Audit Record is created by code
  67.  *                        generated by one compiler, and accessed by code generated
  68.  *                        by a different compiler.
  69.  *    size.auditEntry        The size of the AuditQueueEntry. (See above.)
  70.  *    free.Queue            A standard O.S. queue with available data logging records.
  71.  *    data.queue            A standard O.S. queue with records waiting to be displayed
  72.  *                        or written to a file.
  73.  *    lostData            This counter is incremented when Audit function cannot
  74.  *                        obtain a record from the free queue.
  75.  *    PSN                    Awaken this process when something is stored.
  76.  *    refNum                a void * (longword) that may be used by the calling
  77.  *                        software.
  78.  *    isLogging            TRUE if logging is enabled.
  79.  *    timeAtStart            GetDateTime() when the log was created.
  80.  *    ticksAtStart        TickCount() when the log was created.
  81.  *    entries[]            A vector of AuditEntry records.
  82.  *
  83.  * The audit user accesses the log record by calling functions: it should not
  84.  * access the structure directly.
  85.  *
  86.  * Each log record is stored in a AuditEntry structure that contains the
  87.  * following information:
  88.  *    tickCount            The Ticks value at the time the data was collected.
  89.  *    lostData            This is the number of records that were "lost" before this
  90.  *                        record because there was no free entry in the queue.
  91.  *    idCode                This longword identifies the log entry (i.e. who logged it).
  92.  *    format                This longword describes the format of the log data.
  93.  *    data                This 32 byte data area contains the log. If format is zero,
  94.  *                        the data is a Str31. Otherwise, it contains up to 8
  95.  *                        (sizeof data / sizeof (long)) longwords of data.
  96.  *
  97.  * Your application or driver calls the following functions to manage the log:
  98.  *
  99.  *        AuditPtr            InitAudit(
  100.  *            OSType                gestaltSelector,
  101.  *            short                nEntries
  102.  *            Boolean                initiallyEnabled,
  103.  *            Boolean                preserveFirst
  104.  *        );
  105.  *
  106.  *    Create the log record and "publish" a Gestalt selector with a pointer to the
  107.  *    record. This is normally called by a driver when it is first opened.
  108.  *    gestaltSelector is unique to the driver. InitAudit returns a pointer to the
  109.  *    log record or NULL if it could not create the log. If the gestaltSelector was
  110.  *    already registered, it returns a pointer to the already-created log without
  111.  *    creating a new log.
  112.  *
  113.  *        void                Audit(
  114.  *            AuditPtr            auditPtr,
  115.  *            OSType                idCode,
  116.  *            unsigned long        format,
  117.  *            ...
  118.  *        );
  119.  *
  120.  *    Store data in the log. idCode identifies this log request: by convention, it
  121.  *    is an OSType. The format parameter simplifies display by defining the format
  122.  *    of the subsequent data, The remaining parameters are log dependent. If auditPtr
  123.  *    is NULL or logging disabled, the function does nothing, but does not fail.
  124.  *
  125.  *    The AuditFormat macro is used to specify the format of the stored data:
  126.  *
  127.  *        AuditFormat(arg1, arg2, ..., arg8)
  128.  *
  129.  *    where arg1, etc. is specified by one of the following constants:
  130.  *        kAuditFormatEnd            No more data
  131.  *        kAuditFormatSigned        The datum is a 32-bit signed decimal integer
  132.  *        kAuditFormatUnsigned    The datum is a 32-bit unsigned decimal integer
  133.  *        kAuditFormatHex            The datum is a 32-bit address or 4-byte character
  134.  *        kAuditFormatAddress        The datum is a 32-bit hex value (never characters)
  135.  *        kAuditFormatString        The datum is a Pascal string. This must be the last
  136.  *                                (or only) argument. The string will be truncated
  137.  *                                to fit the remaining space. I.e. if it is the only
  138.  *                                argument, a 31-byte (plus one byte count) string
  139.  *                                will be stored.
  140.  *        kAuditFormatLocation    This must be the last format, but there is no
  141.  *                                associated value. Instead, the library stores
  142.  *                                the calling function name (the MacsBug name).
  143.  *                                The name will be truncated as for kAuditFormatString.
  144.  *
  145.  * Note that AuditFormat1, AuditFormat2, etc. macros are provided to simplify
  146.  * access to AuditFormat().
  147.  *
  148.  * Important: all parameters to Audit must be longwords. Short values (such as
  149.  * Booleans and OSErr codes) must be cast to long or unsigned long:
  150.  *        Audit(auditPtr, 'Err!', AuditFormat1(kAuditFormatSigned),
  151.  *            (long) statusError);
  152.  * If you do not do this, the data may be logged incorrectly or, in extreme
  153.  * cases, your program may crash.
  154.  *
  155.  ***
  156.  *        Boolean                AuditRead(
  157.  *            AuditPtr            auditPtr,
  158.  *            AuditEntry            *thisAuditEntry
  159.  *        );
  160.  *    An application program calls AuditRead periodically to retrieve data from the
  161.  *    log.  If there is a log entry to process, it is copied to thisAuditEntry and
  162.  *    the function returns TRUE. If it returns FALSE, there is no data waiting. This
  163.  *    function should be called from the log application's event loop. AuditRead
  164.  *    manages all log queues. Note that reading the log resets the missedDataCount.
  165.  *
  166.  * Each log record is time-stamped by the following algorithm:
  167.  *        GetAuditStartTimes(auditPtr, &gTimeAtStart, &gTicksAtStart);
  168.  *        if (AuditRead(auditPtr, &thisAuditEntry)) {
  169.  *            elapsedTicks = thisAuditEntry.tickCount - gTicksAtStart;
  170.  *            SecsToDate(
  171.  *                gTimeAtStart + (elapsedTicks / 60),
  172.  *                &logEntryDateString
  173.  *            );
  174.  *            printf(, ..., date.second, elapsedTicks % 60);
  175.  *        }
  176.  * See AuditEntryFormat.c for details.
  177.  *
  178.  ***
  179.  *        AuditPtr            GetAuditPtr(
  180.  *            OSType                gestaltSelector
  181.  *        );
  182.  *
  183.  *    Return a pointer to the common storage area, if it exists. This returns NULL
  184.  *    if there is no log "registered" by that name.
  185.  *
  186.  ***
  187.  *        void                WakeUpAudit(
  188.  *            AuditPtr            auditPtr,
  189.  *            ProcessSerialNumber    *oldPSN
  190.  *        );
  191.  *
  192.  *    Wake up this process (application) when something is logged: called by:
  193.  *            GetCurrentProcess((&oldPSN);
  194.  *            WakeUpAudit(auditPtr, &oldPSN);
  195.  *            ... logging stuff ...
  196.  *            WakeUpAudit(auditPtr, &oldPSN);    // restore previous
  197.  *            ExitToShell();
  198.  *
  199.  ***
  200.  *        Boolean                EnableAudit(
  201.  *            AuditPtr            auditPtr,
  202.  *            Boolean                enableLogging
  203.  *        );
  204.  *
  205.  *    Enable or disable logging. This returns the previous logging value. At
  206.  *    initialization, a compile-time parameter sets the initial logging value.
  207.  *
  208.  ***
  209.  *        Boolean                IsAuditEnabled(
  210.  *            AuditPtr            auditPtr
  211.  *        );
  212.  *
  213.  *    Return TRUE if auditing is enabled for this audit record.
  214.  *
  215.  ***
  216.  *        void                GetAuditStartTimes(
  217.  *            AuditPtr            auditPtr,
  218.  *            unsigned long        *timeAtStart,
  219.  *            unsigned long        *ticksAtStart
  220.  *        );
  221.  *    Return the times that the log was created. Using these values, the log display
  222.  *    application can time-stamp all log entries.
  223.  *
  224.  ***
  225.  *        void                SetAuditRefNum(
  226.  *            AuditPtr            auditPtr,
  227.  *            void                *refNum
  228.  *        );
  229.  * Store a user-controlled reference value. This may be coerced to any scalar
  230.  *    value (such as a memory pointer or longword).
  231.  *
  232.  ***
  233.  *        void                *GetAuditRefNum(
  234.  *            AuditPtr            auditPtr
  235.  *        );
  236.  * Return the current user-controlled reference value. This may be coerced to any
  237.  * scalar value (such as a memory pointer or longword). This returns zero if
  238.  * auditPtr is NULL or no value had been stored.
  239.  */
  240.  
  241. #include "Audit.h"
  242. #include <Types.h>
  243. #include <Traps.h>
  244. #include <Errors.h>
  245. #include <Memory.h>
  246. #include <GestaltEqu.h>
  247. #include <OSUtils.h>
  248. #ifndef PARANOIA
  249. #define PARANOIA    0
  250. #endif
  251.  
  252. /*
  253.  * The version id's are defined as (majorVersion << 8) | minorVersion
  254.  */
  255. #define kAuditEarliestVersion    ((1 << 8) | 2)
  256. #define kAuditLatestVersion        ((1 << 8) | 2)
  257.  
  258. #ifndef TRUE
  259. #define TRUE    1
  260. #define FALSE    0
  261. #endif
  262. /*
  263.  * This is Think C specific, and possibly release dependent.
  264.  */
  265. #if defined(_Gestalt) == FALSE && defined(_GestaltDispatch)
  266. #define _Gestalt    _GestaltDispatch
  267. #endif
  268.  
  269. /*
  270.  * Note that this file contains 68000 assembly. This must be modified when
  271.  * the Audit Library is converted to Power PC.
  272.  */
  273. /*
  274.  * These values must match values in AuditDCMD.c
  275.  */
  276. #define kAuditMagicHeaderSize    7            /* Size in longwords            */
  277. #define kAuditMagicHeader_0        0x4E560000    /* First word in Gestalt result    */
  278.  
  279. #if 0
  280. /*
  281.  * The following sequence was used to create the GestaltSelector stub. It is
  282.  * never compiled directly, but was pasted into a temporary file which was
  283.  * decompiled to get the machine code. 
  284.  */
  285. static pascal OSErr    GestaltSelector(
  286.         OSType            selector,
  287.         long            *result
  288.     )
  289. {
  290.         asm {
  291.             /*
  292.              * 22 was determined by painful observation and many pleasant
  293.              * encounters with MacsBug.
  294.              */
  295.             lea            22(pc),a0        /* a0 = Address of auditRecord        */
  296.             movea.l        result,a1        /* a1 -> result                        */
  297.             move.l        a0,(a1)            /* Store result                        */
  298.         }
  299.         return (noErr);
  300. }
  301. #endif
  302.  
  303. /*
  304.  * DisableInterrupts and EnableInterrupts manipulate the 68000 status register.
  305.  * They are expanded inline. A comment consisting of *** in the left margin
  306.  * indicates code that is run with interrupts disabled.
  307.  *
  308.  * unsigned short DisableInterrupts(void) {
  309.  *        asm {
  310.  *            move        sr,d0
  311.  *            move        #0x2600,sr
  312.  *        }
  313.  */
  314. unsigned short DisableInterrupts(void) = { 0x40C0, 0x46FC, 0x2600 };
  315. #ifdef THINK_C
  316. /*
  317.  * Since Think C has a real inline assembler, we can save one or two instructions.
  318.  * Nerd fun.
  319.  */
  320. #define EnableInterrupts(saveSR) asm {            \
  321.             move        saveSR,sr                \
  322.         }
  323. #else
  324. /*
  325.  * Even though the saveSR argument is a short, we must declare the function as a
  326.  * long so that both Think and MPW generate the same code. Otherwise, Think pushes
  327.  * a short and 2(sp) addresses the wrong value. (Guess how I discovered this.)
  328.  * Using the Think C inline defined above also eliminates the problem.
  329.  *
  330.  * void EnableInterrupts(unsigned long saveSR) {
  331.  *        asm {
  332.  *            move        2(sp),sr
  333.  *        }
  334.  */
  335. void EnableInterrupts(unsigned long saveSR) = { 0x46EF, 0x0002 };
  336. #endif
  337.  
  338. /*
  339.  * This inline copies the value of A6 (the frame pointer) to D0. It is needed in
  340.  * order to get the caller's function name.
  341.  */
  342. unsigned long *GetA6(void) = { 0x200E };    /* move.l A6,D0    */
  343.  
  344. #define LOG        (*auditPtr)
  345. #define ENTRY    (entryPtr->theEntry)
  346.  
  347. static void                            StoreString(
  348.         const StringPtr        theString,        /* Source, length may be wrong    */
  349.         unsigned short        stringLength,    /* Purported actual length        */
  350.         unsigned short        maxLength,        /* Maximum destination length    */
  351.         StringPtr            result            /* Destination pointer            */
  352.     );
  353. static AuditQueueEntryPtr            GetQueueEntry(
  354.         QHdrPtr                    theQueue
  355.     );
  356. #define PutQueueEntry(theQueue, theEntry) do {                \
  357.         Enqueue((QElemPtr) &theEntry->qLink, theQueue);        \
  358.     } while (0)
  359.  
  360. static Boolean                        TrapAvailable(
  361.         short                    theTrap
  362.     );
  363. static void                            GetCallerName(
  364.         unsigned long            *destPtr,
  365.         unsigned short            maxString,
  366.         unsigned long            *a6
  367.     );
  368.  
  369. /*
  370.  * InitAudit
  371.  */
  372. AuditPtr
  373. InitAudit(
  374.         OSType                    gestaltSelector,
  375.         unsigned short            nEntries,
  376.         Boolean                    initiallyEnabled,
  377.         Boolean                    preserveFirst
  378.     )
  379. {
  380.         register unsigned long        *recordPtr;
  381.         register AuditPtr            auditPtr;
  382.         register AuditQueueEntryPtr    queueEntryPtr;
  383.         Size                        logRecordSize;
  384.         short                        i;
  385.         OSErr                        status;
  386.         
  387.         /*
  388.          * This will not compile on either Think or MPW if the sizes are correct.
  389.          * Unfortunately, sizeof cannot be incorporated into a #define, so we
  390.          * have to do this at execution time.
  391.          */
  392.         if (sizeof (AuditEntry) != kSizeofAuditEntry)
  393.             DebugStr("\pAuditEntry size error");
  394.         if (sizeof (AuditQueueEntry) != kSizeofAuditQueueEntry)
  395.             DebugStr("\pAuditQueueEntry size error");
  396.         if (sizeof (AuditRecord) != kSizeofAuditRecord)
  397.             DebugStr("\pAuditRecord size error");
  398.         /*
  399.          * Presuppose problems and exit if the system is so old that it doesn't
  400.          * even support Gestalt. This is slightly overkill, as the current
  401.          * Gestalt glue does the TrapAvailable check for us.
  402.          */
  403.         auditPtr = NULL;
  404.         if (TrapAvailable(_Gestalt) == FALSE)
  405.             goto exit;
  406.         logRecordSize = sizeof (AuditRecord)
  407.                     + (kAuditMagicHeaderSize * sizeof (long))
  408.                     + ((nEntries - 1) * sizeof (AuditQueueEntry));
  409. #if PARANOIA > 1
  410.         {
  411.             Str255                    foo;
  412.             NumToString(sizeof (AuditQueueEntry), foo);
  413.             DebugStr(foo);
  414.             NumToString(logRecordSize, foo);
  415.             DebugStr(foo);
  416.         }
  417. #endif
  418.         recordPtr = (unsigned long *) NewPtrSysClear(logRecordSize);
  419.         if (recordPtr == NULL)
  420.             goto exit;
  421.         auditPtr = (AuditPtr) &recordPtr[kAuditMagicHeaderSize];
  422.         /*
  423.          * The code for the Gestalt Selector function precedes the actual audit
  424.          * record. Gestalt calls this function when a program calls GetAuditPtr
  425.          * for our selector. The function returns the address of the AuditRecord
  426.          * as the selector value. This code will need revision when run on
  427.          * non-Motorola cpu's. See the definition of GestaltSelector above for
  428.          * the original code.
  429.          *
  430.          * asm {
  431.          *        link        a6,#0000    Create stack frame    0x4E56 0x0000
  432.          *        lea            22(pc),a0    a0 -> auditRecord    0x41FA 0x0016  
  433.          *        movea.l        8(a6),a1    a1 -> response        0x226E 0x0008
  434.          *        move.l        a0,(a1)        Store result        0x2288
  435.          *        clr.w        8(a6)        Result := noErr        0x426E 0x0010
  436.          *        unlk        a6            Delete stack frame    0x4E5E
  437.          *        movea.l        (a7)+,a0    Pop return address    0x205F
  438.          *        addq.l        #0x8,a7        Clear stack params    0x508F
  439.          *        jmp            (a0)        Return to caller    0x4ED0
  440.          *        dc.w        0            Fake MacsBug name    0x0000
  441.          *        ** AuditRecord starts here **
  442.          *    };
  443.          */
  444. #if kAuditMagicHeader_0 != 0x4E560000
  445.     << error, the following won't work: see AuditDCMD.c >>
  446. #endif
  447.         recordPtr[0] = 0x4E560000;
  448.         recordPtr[1] = 0x41FA0016;
  449.         recordPtr[2] = 0x226E0008;
  450.         recordPtr[3] = 0x2288426E;
  451.         recordPtr[4] = 0x00104E5E;
  452.         recordPtr[5] = 0x205F508F;
  453.         recordPtr[6] = 0x4ED00000;        /* Pad with fake MacsBug name    */
  454. #if kAuditMagicHeaderSize != 7
  455.     << error, the above won't work: see AuditDCMD.c >>
  456. #endif
  457.         /*
  458.          * If this machine has a data/instruction cache, flush it so that we
  459.          * cannot execute stale data.
  460.          */
  461.         if (TrapAvailable(_HWPriv)) {
  462.             FlushDataCache();
  463.             FlushInstructionCache();
  464.         }
  465.         /*
  466.          * Add this as a gestalt. Errors present problems: either this was already
  467.          * added (no big deal), or the luser is trying to redefine a "normal"
  468.          * Gestalt. We check by trying to get the AuditPtr and hope that
  469.          * GetAuditPtr's internal checks ferret out an attempt to redefine a
  470.          * normal Gestalt.
  471.          */
  472.         status = NewGestalt(gestaltSelector, (ProcPtr) recordPtr);
  473.         if (status == gestaltDupSelectorErr) {
  474.             /*
  475.              * Trouble! this has already been added! Your program
  476.              * may eventually crash if the gestalt is not an audit
  477.              * record: see GetAuditPtr() for the safety checks.
  478.              */
  479.             DisposPtr((Ptr) auditPtr);
  480.             auditPtr = GetAuditPtr(gestaltSelector);
  481.             goto exit;
  482.         }
  483.         else if (status != noErr) {
  484.             /*
  485.              * Something is seriously out of order. Just return NULL.
  486.              */
  487.             DisposPtr((Ptr) recordPtr);
  488.             auditPtr = NULL;
  489.             goto exit;
  490.         }
  491.         /*
  492.          * Setup the rest of the AuditRecord.
  493.          */
  494.         LOG.version.u.low = kAuditEarliestVersion;
  495.         LOG.version.u.high = kAuditLatestVersion;
  496.         LOG.recordSize = kAuditRecordSize;
  497.         /*
  498.          * Store the top memory location in the AuditRecord. This helps protect
  499.          * us from crashes in Audit if the caller passes a garbage StringPtr.
  500.          */
  501.         Gestalt(gestaltLogicalRAMSize, (long *) &LOG.logicalRAMSize);
  502.         GetDateTime(&LOG.timeAtStart);
  503.         LOG.ticksAtStart = TickCount();
  504.         LOG.PSN.lowLongOfPSN = kNoProcess;
  505.         /*
  506.          * Ok, so far. Now build the initial free queue.
  507.          */
  508.         queueEntryPtr = &LOG.entries[0];
  509.         for (i = 0; i < nEntries; i++) {
  510.             PutQueueEntry(&LOG.free.queue, queueEntryPtr);
  511.             ++queueEntryPtr;
  512.         }
  513. exit:    EnableAudit(auditPtr, initiallyEnabled);
  514.         PreserveAudit(auditPtr, preserveFirst);
  515.         return (auditPtr);
  516. }
  517.  
  518. /*
  519.  * This function logs data if logging is enabled.
  520.  *
  521.  *        auditPtr    As returned by InitAudit. If NULL, nothing is logged.
  522.  *        idCode        A user-controlled value, by convention an OSType (4-byte
  523.  *                    character), that identifies this entry. The display program
  524.  *                    prints it.
  525.  *        format        The format of the remaining data: kAuditFormatString if the
  526.  *                    datum is a pascal string, otherwise, it is as created by the
  527.  *                    AuditFormat macro.
  528.  *        ...            Additional data as needed. These values must be cast to
  529.  *                    longwords to prevent ThinkC/MPW incompatibilities.
  530.  */
  531. void
  532. Audit(
  533.         register AuditPtr        auditPtr,
  534.         OSType                    idCode,
  535.         unsigned long            format,
  536.         ...
  537.     )
  538. {
  539.         unsigned short                saveSR;
  540.         va_list                        argPtr;
  541.         register unsigned long        *destPtr;
  542.         unsigned long                tickCount;
  543.         unsigned short                maxString;
  544.         unsigned short                thisFormat;
  545.         StringPtr                    theString;
  546.         unsigned char                hexString[10];
  547.         register AuditQueueEntryPtr    entryPtr;
  548.         register AuditQueueEntryPtr    nextPtr;
  549.  
  550.         tickCount = TickCount();
  551.         if (auditPtr != NULL && (LOG.flags & kAuditEnabledMask) != 0) {
  552.             entryPtr = GetQueueEntry(&LOG.free.queue);
  553.             /*
  554.              * Note: *** in the left-most column indicates code that is executed
  555.              * with interrupts disabled.
  556.              */
  557.             saveSR = DisableInterrupts();
  558. /***/        if (entryPtr == NULL) {
  559. /***/            ++LOG.lostData;
  560. /***/            EnableInterrupts(saveSR);
  561.                 if ((LOG.flags & kAuditPreserveFirstMask) == 0) {
  562.                     /*
  563.                      * We really want to log the latest entry. Remove the first
  564.                      * "to be displayed" entry. If successful, it will get the new
  565.                      * record and the next "to be displayed" entry, if any, gets
  566.                      * the lost data counter.
  567.                      */
  568.                     entryPtr = GetQueueEntry(&LOG.data.queue);
  569.                     if (entryPtr == NULL)
  570.                         ;                /* goto return        */
  571.                     else {
  572. /***/                    saveSR = DisableInterrupts();
  573. /***/                    LOG.lostData += ENTRY.lostData;
  574. /***/                    nextPtr = (AuditQueueEntryPtr) LOG.data.queue.qHead;
  575. /***/                    if (nextPtr != NULL) {
  576. /***/                        nextPtr->theEntry.lostData += LOG.lostData;
  577. /***/                        LOG.lostData = 0;
  578. /***/                    }
  579. /***/                    goto returnThisEntry;
  580.                     }
  581.                 }
  582.             }
  583. /***/        else {
  584. returnThisEntry:
  585. /***/            ENTRY.lostData = LOG.lostData;
  586. /***/            LOG.lostData = 0;
  587. /***/            EnableInterrupts(saveSR);
  588.                 ENTRY.tickCount = tickCount;
  589.                 ENTRY.idCode = idCode;
  590.                 ENTRY.format = format;
  591.                 va_start(argPtr, format);
  592.                 destPtr = ENTRY.data;
  593.                 maxString = sizeof (ENTRY.data);
  594.                 while (maxString > 0) {
  595.                     thisFormat = format & kAuditFormatMask;
  596.                     switch (thisFormat) {
  597.                     case kAuditFormatEnd:
  598.                         goto exitLoop;
  599.                     case kAuditFormatString:
  600.                         theString = va_arg(argPtr, StringPtr);
  601.                         /*
  602.                          * Make sure this is a real string pointer. Otherwise, if
  603.                          * it's bogus, theString[0] will cause an address error.
  604.                          */
  605.                         theString = (StringPtr) StripAddress((Ptr) theString);
  606.                         if (theString == NULL
  607.                          || (unsigned long) theString >= LOG.logicalRAMSize) {
  608.                             short                i;
  609.                             unsigned short        value;
  610.  
  611.                             /*
  612.                              * Bogus string pointer (NULL or otherwise extreme):
  613.                              * store the Hex address in the audit record.
  614.                              */
  615.                             for (i = 10; i > 1; i--) {
  616.                                 value = ((unsigned long) theString) & 0x0F;
  617.                                 *((unsigned long *) &theString) >>= 4;
  618.                                 hexString[i] = (value < 10)
  619.                                             ? value + '0' : value + ('A' - 10);
  620.                             }
  621.                             hexString[1] = '?';
  622.                             hexString[0] = 9;
  623.                             theString = hexString;
  624.                         }
  625.                         StoreString(
  626.                             theString,
  627.                             theString[0],
  628.                             maxString,
  629.                             (StringPtr) destPtr
  630.                         );
  631.                         goto exitLoop;
  632.                     case kAuditFormatLocation:
  633.                         GetCallerName(destPtr, maxString, GetA6());
  634.                         goto exitLoop;
  635.                     default:
  636.                         *destPtr++ = va_arg(argPtr, unsigned long);
  637.                         format >>= kAuditFormatShift;
  638.                         maxString -= sizeof (unsigned long);
  639.                         break;
  640.                     }
  641.                 }
  642. exitLoop:        va_end(nextArg);
  643.                 PutQueueEntry(&LOG.data.queue, entryPtr);
  644.                 if (LOG.PSN.highLongOfPSN != 0
  645.                  || LOG.PSN.lowLongOfPSN != kNoProcess)
  646.                     (void) WakeUpProcess(&LOG.PSN);
  647.             }
  648.         }
  649. #undef DATA
  650. }
  651.  
  652. /*
  653.  * The following functions are normally called by the display application:
  654.  */
  655.  
  656. /*
  657.  * Return a pointer to the log area, or NULL if there is none. Note that we make
  658.  * a few sanity checks to better ensure that the gestaltSelector actually refers
  659.  * to a Audit record.
  660.  */
  661. AuditPtr
  662. GetAuditPtr(
  663.         OSType                    gestaltSelector
  664.     )
  665. {
  666.         auto long                gestaltResponse;
  667.         register AuditPtr        auditPtr;
  668.  
  669.         if (TrapAvailable(_Gestalt) == FALSE
  670.          || Gestalt(gestaltSelector, &gestaltResponse) != noErr
  671.          || gestaltResponse == 0
  672.          || (gestaltResponse & 0x3) != 0)
  673.              auditPtr = NULL;
  674.         else {
  675.             auditPtr = (AuditPtr) gestaltResponse;
  676.             /*
  677.              * Sanity check: make sure the result is really an Audit Record and
  678.              * we can deal with the library release that created it.
  679.              */
  680.             if (((unsigned long *) auditPtr)[-kAuditMagicHeaderSize]
  681.                     != kAuditMagicHeader_0
  682.              || LOG.version.u.low < kAuditEarliestVersion
  683.              || LOG.version.u.high > kAuditLatestVersion
  684.              || LOG.recordSize != kAuditRecordSize)
  685.                 auditPtr = NULL;
  686.         }
  687.         return (auditPtr);
  688. }
  689.  
  690. /*
  691.  * Awaken a specified process. Call by the following sequence:
  692.  *        GetCurrentProcess(&oldPSN);        // process to awaken
  693.  *        WakeUpAudit(auditPtr, &oldPSN);
  694.  *    The previous process is now stored in oldPSN)
  695.  *        ... display the log ...
  696.  *        WakeUpAudit(auditPtr, &oldPSN);    // restore old
  697.  *        ExitToShell();
  698.  */
  699. void
  700. WakeUpAudit(
  701.         register AuditPtr        auditPtr,
  702.         ProcessSerialNumber        *oldPSN
  703.     )
  704. {
  705.         ProcessSerialNumber        previousPSN;
  706.         short                    saveSR;
  707.         long                    gestaltResult;
  708.         OSErr                    status;
  709.         
  710.         if (auditPtr != NULL) {
  711.             status = Gestalt(gestaltOSAttr, &gestaltResult);
  712.             if (status != noErr
  713.              || (gestaltResult & (1 << gestaltLaunchControl)) == 0) {
  714.                 oldPSN->highLongOfPSN = 0;
  715.                 oldPSN->lowLongOfPSN = kNoProcess;
  716.             }
  717. /***/        saveSR = DisableInterrupts();
  718. /***/        previousPSN = LOG.PSN;
  719. /***/        LOG.PSN = *oldPSN;
  720. /***/        EnableInterrupts(saveSR);
  721.             *oldPSN = previousPSN;
  722.         }
  723. }
  724.  
  725. /*
  726.  * Enable/disable logging. Returns the old logging state.
  727.  */
  728. Boolean
  729. EnableAudit(
  730.         register AuditPtr        auditPtr,
  731.         Boolean                    enableLogging
  732.     )
  733. {
  734.         Boolean                    oldLogState;
  735.         short                    saveSR;
  736.         
  737.         if (auditPtr == NULL)
  738.             oldLogState = FALSE;
  739.         else {
  740. /***/        saveSR = DisableInterrupts();
  741. /***/        oldLogState = (LOG.flags & kAuditEnabledMask) != 0;
  742. /***/        LOG.flags &= ~kAuditEnabledMask;
  743. /***/        if (enableLogging)
  744. /***/            LOG.flags |= kAuditEnabledMask;
  745. /***/        EnableInterrupts(saveSR);
  746.         }
  747.         return (oldLogState);
  748. }
  749.  
  750. /*
  751.  * Return the value of the Audit enable flag. Returns FALSE if auditPtr is NULL
  752.  * or auditing is disabled.
  753.  */
  754. Boolean
  755. IsAuditEnabled(
  756.         AuditPtr                auditPtr
  757.     )
  758. {
  759.         if (auditPtr == NULL)
  760.             return (FALSE);
  761.         else {
  762.             return ((LOG.flags & kAuditEnabledMask) != 0);
  763.         }
  764. }
  765.  
  766. /*
  767.  * Enable/disable logging. Returns the old logging state.
  768.  */
  769. Boolean
  770. PreserveAudit(
  771.         register AuditPtr        auditPtr,
  772.         Boolean                    preserveFirst
  773.     )
  774. {
  775.         Boolean                    oldPreserveState;
  776.         short                    saveSR;
  777.         
  778.         if (auditPtr == NULL)
  779.             oldPreserveState = FALSE;
  780.         else {
  781. /***/        saveSR = DisableInterrupts();
  782. /***/        oldPreserveState = (LOG.flags & kAuditPreserveFirstMask) != 0;
  783. /***/        LOG.flags &= ~kAuditPreserveFirstMask;
  784. /***/        if (preserveFirst)
  785. /***/            LOG.flags |= kAuditPreserveFirstMask;
  786. /***/        EnableInterrupts(saveSR);
  787.         }
  788.         return (oldPreserveState);
  789. }
  790.  
  791. /*
  792.  * Get the time that the log record was created. This is used to time-stamp log
  793.  * entries. Since the time-stamp is created only when the log is created (and
  794.  * before it is visible through Gestalt), we don't need to lock out interrupts.
  795.  */
  796. void
  797. GetAuditStartTimes(
  798.         register AuditPtr        auditPtr,
  799.         unsigned long            *timeAtStart,
  800.         unsigned long            *ticksAtStart
  801.     )
  802. {
  803.         if (auditPtr == NULL) {
  804.             *timeAtStart = 0;
  805.             *ticksAtStart = 0;
  806.         }
  807.         else {
  808.             *timeAtStart = LOG.timeAtStart;
  809.             *ticksAtStart = LOG.ticksAtStart;
  810.         }
  811. }
  812.  
  813. /*
  814.  * Read the next log entry. This returns a copy of the log entry, if one is
  815.  * available and returns TRUE. This function manages all log queues. If no entry
  816.  * is available, or logging is disabled, the function returns FALSE.
  817.  */
  818. Boolean
  819. ReadAudit(
  820.         register AuditPtr        auditPtr,
  821.         AuditEntryPtr            thisAuditEntry
  822.     )
  823. {
  824.         register AuditQueueEntryPtr    entryPtr;
  825.         
  826.         if (auditPtr == NULL)
  827.             return (FALSE);
  828.         else {
  829.             entryPtr = GetQueueEntry(&LOG.data.queue);
  830.             if (entryPtr == NULL)
  831.                 return (FALSE);
  832.             else {
  833.                 *thisAuditEntry = ENTRY;
  834.                 PutQueueEntry(&LOG.free.queue, entryPtr);
  835.                 return (TRUE);
  836.             }
  837.         }
  838. }
  839.  
  840. /*
  841.  * Store a user-controlled reference value. This may be coerced to any scalar
  842.  * value (such as a memory pointer or longword). SetAuditRefNum returns the
  843.  * current value of the refNum, or zero if auditPtr is NULL.
  844.  */
  845. void
  846. *SetAuditRefNum(
  847.         AuditPtr                auditPtr,
  848.         void                    *refNum
  849.     )
  850. {
  851.         void                *result;
  852.         short                    saveSR;
  853.         
  854.         if (auditPtr == NULL)
  855.             result = NULL;
  856.         else {
  857. /***/        saveSR = DisableInterrupts();
  858. /***/        result = LOG.refNum;
  859. /***/        LOG.refNum = refNum;
  860. /***/        EnableInterrupts(saveSR);
  861.         }
  862.         return (result);
  863. }
  864.  
  865. /*
  866.  * Return the current user-controlled reference value. This may be coerced to any
  867.  * scalar value (such as a memory pointer or longword). This returns zero if
  868.  * auditPtr is NULL or no value had been stored.
  869.  */
  870. void *
  871. GetAuditRefNum(
  872.         AuditPtr                auditPtr
  873.     )
  874. {
  875.         /*
  876.          * Note that we assume that we can retrieve LOG.refNum without
  877.          * interference from interrupt routines. Is this reasonable?
  878.          */
  879.         return ((auditPtr == NULL) ? NULL : LOG.refNum);
  880. }
  881.  
  882. /*
  883.  * Remove the first entry from the queue, return NULL on failure.
  884.  */
  885. static AuditQueueEntryPtr
  886. GetQueueEntry(
  887.         QHdrPtr                    theQueue
  888.     )
  889. {
  890.         register QElemPtr        qElemPtr;
  891.         
  892.         if ((qElemPtr = theQueue->qHead) != NULL) {
  893.             if (Dequeue(qElemPtr, theQueue) != noErr)
  894.                 qElemPtr = NULL;
  895.         }
  896.         return ((AuditQueueEntryPtr) qElemPtr);
  897. }
  898.  
  899. /*
  900.  * TrapAvailable (see Inside Mac VI 3-8)
  901.  */
  902. #define NumToolboxTraps() (                                \
  903.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  904.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  905.             ? 0x200 : 0x400                                \
  906.     )
  907. #define GetTrapType(theTrap) (                            \
  908.         ((theTrap) & 0x800 != 0) ? ToolTrap : OSTrap    \
  909.     )
  910.  
  911. static Boolean
  912. TrapAvailable(
  913.         short                    theTrap
  914.     )
  915. {
  916.         TrapType                trapType;
  917.         
  918.         trapType = GetTrapType(theTrap);
  919.         if (trapType == ToolTrap) {
  920.             theTrap &= 0x07FF;
  921.             if (theTrap >= NumToolboxTraps())
  922.                 theTrap = _Unimplemented;
  923.         }
  924.         return (
  925.             NGetTrapAddress(theTrap, trapType)
  926.             != NGetTrapAddress(_Unimplemented, ToolTrap)
  927.         );
  928. }
  929.  
  930. /*
  931.  * GetCallerName peeks up the calling chain list to the Audit function caller. It
  932.  * then scans through the function code to locate the start and end of the
  933.  * function. The end of the function is followed by the MacsBug name (maybe).
  934.  * This is based on ShowStackChain.c by Greg Anderson greggor@apple.com
  935.  */
  936. static void
  937. GetCallerName(
  938.         unsigned long            *destPtr,
  939.         unsigned short            maxString,
  940.         unsigned long            *a6
  941.     )
  942. {
  943.         unsigned long                returnAddress;
  944.         register unsigned short        *pc;
  945.         short                        maxSearch;
  946.         unsigned short                length;
  947.         Str31                        tempString;
  948.         unsigned long                pcOffset;
  949. #define pcByte        ((unsigned char *) pc)
  950.  
  951.         returnAddress = a6[1];            /* Return address to Audit caller    */
  952.         /*
  953.          * First, look forwards for the symbol name
  954.          */
  955.         pc = (unsigned short *) returnAddress;
  956.         tempString[0] = 0;
  957.         for (maxSearch = 0; maxSearch < 0x4000; maxSearch++, pc++) {
  958.             if (pc[0] == 0x4E56)        /* link                                */
  959.                 break;                    /* We've gone too far                */
  960.             if (pc[0] == 0x4E75            /* rts                                */
  961.               || pc[0] == 0x4ED0        /* jmp (a0)                            */
  962.               || pc[0] == 0x4E74) {        /* rtd #N                            */
  963.                   if (pc[0] == 0x4E74)    /* If rtd, skip over displacement    */
  964.                     pc++;
  965.                 pc++;                    /* Skip over return instruction        */
  966.                  if (pc[0] != 0x4E56) {    /* Link instruction means no name    */
  967.                      /*
  968.                       * pc now points to the word following the RTS. Use the
  969.                       * MacsBug naming conventions to determine the function name,
  970.                       * if any. See Appendix D in the MacsBug User's Guide.
  971.                       */
  972.                       length = pcByte[0] & 0x7F;
  973.                       if (length >= 0x20 && length <= 0x7F) {
  974.                          /*
  975.                           * Fixed length format: first byte is in the range 0x20
  976.                           * through 0x7F, the high bit may or may not be set.
  977.                           */
  978.                           if ((pcByte[1] & 0x80) != 0) {
  979.                               /*
  980.                                * Pascal 16-byte class name format. The string is
  981.                                * stored "method" "class" (each takes 8 bytes) --
  982.                                * MacsBug swaps the order and inserts a '.'
  983.                                * (Warning, this is untested.)
  984.                                */
  985.                               BlockMove(&pcByte[0], &tempString[10], 8);
  986.                               BlockMove(&pcByte[7], &tempString[1], 8);
  987.                               tempString[10] &= ~0x80;
  988.                               tempString[11] &= ~0x80;
  989.                               tempString[9] = '.';
  990.                               tempString[0] = 17;
  991.                           }
  992.                           else {
  993.                               /*
  994.                                * Pascal 8 byte format.
  995.                                */
  996.                               BlockMove(&pcByte[0], &tempString[1], 8);
  997.                               tempString[0] = 7;
  998.                           }
  999.                     }
  1000.                     else if (pcByte[0] >= 0x80 && pcByte[0] <= 0x9F) {
  1001.                         /*
  1002.                          * Variable-length string format. If the length byte is
  1003.                          * zero after removing the flag bit, the next byte has
  1004.                          * the true (Str255) length.
  1005.                          */
  1006.                         if (length == 0) {
  1007.                             /*
  1008.                              * Step over the flag. Note that pc and pcByte are the
  1009.                              * same variable (pc is a "short *" while pcByte is a
  1010.                              * "char *"). The next statement adds 1 to pc in a way
  1011.                              * that doesn't cause ANSI compilers to lose their
  1012.                              * electronic lunches.
  1013.                              */
  1014.                             pc = (unsigned short *) &pcByte[1];
  1015.                             length = pcByte[0];
  1016.                         }
  1017.                         StoreString(
  1018.                             pcByte,
  1019.                             length,
  1020.                             sizeof (tempString) - 1,
  1021.                             tempString
  1022.                         );
  1023.                     }
  1024.                     else {
  1025.                         /*
  1026.                          * Something isn't understandable. Do nothing. This will
  1027.                          * be caught by the "if there is no symbol" test below.
  1028.                          */
  1029.                     }
  1030.                 }
  1031.                 break;
  1032.             }
  1033.         }
  1034.         /*
  1035.          * If there is no symbol name, don't search for an offset.
  1036.          */
  1037.         pcOffset = 0;
  1038.         if (tempString[0] != 0) {
  1039.             pc = (unsigned short *) returnAddress;
  1040.             for (maxSearch = 0; maxSearch < 0x4000; maxSearch++) {
  1041.                 if (pc[0] == 0x4E56) {                /* link instruction    */
  1042.                     pcOffset =
  1043.                         ((unsigned long) returnAddress)
  1044.                         - ((unsigned long) pc);
  1045.                     break;
  1046.                 }
  1047.                 --pc;
  1048.             }
  1049.         }
  1050.         if (pcOffset == 0 || tempString[0] == 0) {
  1051.             /*
  1052.              * Since we don't have a MacsBug name, set the offset value to zero
  1053.              * (this will act as a signal) and store  the return address in the
  1054.              * second data value.
  1055.              */
  1056.             if (maxString >= sizeof (unsigned long)) {
  1057.                 *destPtr++ = 0;
  1058.                 maxString -= sizeof (unsigned long);
  1059.             }
  1060.             if (maxString >= sizeof (unsigned long)) {
  1061.                 *destPtr++ = returnAddress;
  1062.                 maxString -= sizeof (unsigned long);
  1063.             }
  1064.         }
  1065.         else {
  1066.             if (maxString >= sizeof (unsigned long)) {
  1067.                 *destPtr++ = pcOffset;
  1068.                 maxString -= sizeof (unsigned long);
  1069.                 StoreString(
  1070.                     tempString,
  1071.                     tempString[0],
  1072.                     maxString,
  1073.                     (StringPtr) destPtr
  1074.                 );
  1075.             }
  1076.         }
  1077. #undef pcByte
  1078. }
  1079.  
  1080. /*
  1081.  * Copy the source string to the destination, respecting the maximum size.
  1082.  */
  1083. static void
  1084. StoreString(
  1085.         const StringPtr        theString,
  1086.         unsigned short        stringLength,
  1087.         unsigned short        maxLength,
  1088.         StringPtr            result
  1089.     )
  1090. {
  1091.         if (stringLength >= maxLength)
  1092.             stringLength = maxLength - 1;
  1093.         BlockMove(theString, result, stringLength + 1);
  1094.         result[0] = stringLength;
  1095. }
  1096.             
  1097.  
  1098.